/* License Notice:
**
** This program is free software: you can redistribute it and/or modify
**    it under the terms of the GNU General Public License as published by
**    the Free Software Foundation, either version 3 of the License, or
**    (at your option) any later version.
** This program is distributed in the hope that it will be useful,
**   but WITHOUT ANY WARRANTY; without even the implied warranty of
**   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
**   GNU General Public License for more details.
** You should have received a copy of the GNU General Public License
**   along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

/**
 * @file character.hpp
 * @author TooOld2Rock'nRoll
 * @date 2023/02/07
 * @brief Implements a suggestion for what a playable character supper class could be for the ArcadeFighter library.
 *
 * @todo Chain actions use only one final link to execute an action, we could use intermediate states to do smaller actions. For instance, A button is one attack and B another, but A->B could do a different combo or animation sequence.
 * @todo Facing seams like behavior, review the mechanics to make more sense....
 * @todo DemoCharacter has a short_jump command, we may be able to simplify  the procedure by setting horizontal speed on walking and using just the jump command.
 * @todo Need to set a character sheet, to finish statistics struct.
 * @todo Load a entire character form a file.
 * @todo All "physics" is now hardcoded to look good on screen, need a RPG style system to govern reaction and movement.
 */
#ifndef _CHARACTER_HPP_
#define _CHARACTER_HPP_

/*---- Includes ----*/
#include "interfaces/command.hpp"
#include "interfaces/input_device.hpp"
#include "interfaces/state.hpp"
#include "input_devices/controller.hpp"

#include "properties.hpp"
#include "toolbox/nary_tree_simple.hpp"

#include "audio/audio.hpp"
#include "event_manager.hpp"
#include "interfaces/game_object.hpp"


/*---- Enumerations ----*/
extern "C" {
/**
 * @brief List of expected Character states.
 * We made it a C style enumeration (no strong type), so you can extend it freely or overwrite it completely and
 *    still be able to override this class virtual methods. Remember, enums in C are just constant integers!<br>
 * You may do so by something like the following example and when you override setState() you just pass original or
 *    extended values interchangeably:
 * @code
 *    typedef enum _extended_char_state_
 *    {
 *        MY_FIRST_STATE = character_state_t::_state_max, //<-- your enumeration will start where character_state ended
 *        MY_SECOND_STATE,
 *        ...
 *        _extend_state_max
 *    } extended_character_state_t;
 * @endcode
 */
typedef enum _char_state_
{
    LOADING = 0, ///< Maybe a level loading animation?
    OPENING_ANIME, ///< Should cycle thru the opening animation and stop.
    WIN_ANIME, ///< Should cycle thru the winner animation and stop.
    LOSE_ANIME, ///< Should cycle thru the loser animation and stop.
    STOP, ///< Freezes the character state, use it for Level Pause.
    IDLE, ///< Change to idle animation.
    WALKING, ///< Character is moving slowly.
    CROUCHING, ///< Character is crouching.
    JUMPING, ///< Character is in the middle of a jump.
    BLOCKING, ///< Character is blocking.
    ATTACKING, ///< Character is attacking.
    _state_max ///< Just to keep count of enum size.
} character_state_t;

/**
 * @brief List of expected Character commands.
 * We made it a C style enumeration (no strong type), so you can extend it freely or overwrite it completely and
 *    still be able to override this class virtual methods. Remember, enums in C are just constant integers!<br>
 * You may do so by something like the following example and when you need to expand it you just use the original or
 *    extended values interchangeably:
 * @code
 *    typedef enum _extended_commands_
 *    {
 *        MY_FIRST_COMMAND = character_commands_t::_command_max, //<-- your enumeration will start where character_commands ended
 *        MY_SECOND_COMMAND,
 *        ...
 *        _extend_command_max
 *    } extended_character_commands_t;
 * @endcode
 */
typedef enum _commands_
{
    WALK = 0, ///< Makes the Character Walk
    JUMP, ///< Makes the Character Jump
    _command_max ///< Just to keep count of enum size.
} character_commands_t;
} //extern "C"


/*---- Class Declaration ----*/
/**
 * @brief Character class for the ArcadeFighter library.
 * @extends GameObject
 *
 * This class offers a basic interface for a simple playable character.<br>
 * You are encouraged to extend this class with specific behavior for each of your characters.<br>
 * The above enumerations lists a minimalist set of states and, most likely required, commands to implement in your
 *   character specializations. States being a statement of what the character is doing right now and how it should
 *   behave in regard to the player interaction; And commands being the actual actions that creates the expected on
 *   screen effects.<br>
 *
 * Regarding state changes, we recommend that single button events should be handled by the current state and promptly
 *   back to idle if at all possible, ensuring a smooth flow from one "simple" state to the next.<br>
 * Mapping chain actions is another way of creating state changes, but this time based on longer sequences of inputs,
 *   creating "special moves" or combos.<br>
 */
class Character : public GameObject
{
    public:
        ///Which side is the character facing?
        enum facing_e
        {
            FACE_LEFT = 0, ///< Facing Left, all commands will be reversed!
            FACE_RIGHT     ///< Facing Right.
        };//end facing


        ///Under construction....
        struct statistics_s
        {
            //-- potential --//
            unsigned u_walking_speed = 0; ///<Walking speed (forward/backward) in pixels/sec
            unsigned u_jumping_speed = 0; ///<Jumping speed (consequently height of jump) in pixels/sec

            ///@todo This regulates how fast a chain action may be executed, probably just one of those is useful.
            ///Based on the character speed, we can regulate how fast it can accept new inputs from the player and
            ///   this is probably the best way to regulate the chain action resets over time.
            float f_chain_action_time_out = 0.f; ///<How long to wait for the next event in a chain in sec.
            float f_chain_action_cool_down = 0.f; ///<How long to wait before accepting a new event in a chain in sec.

            //-- imediate state --//
            float f_current_horizontal_speed = 0; ///<The imediate horizontal speed in px/sec (jumping, falling, thrown, etc)
            float f_current_vertical_speed = 0; ///<The imediate vertical speed in px/sec (jumping, falling, thrown, etc)
        };//end statistics


    private:
        //how many audio effects is this class coordinating
        unsigned ui_audio_count = 0;
        //The list of audio effects to use for the player actions.
        Audio::Effect *ap_audio_effects = nullptr;


    protected:
        ///Extends the controller event struct by checking if a link in the chain is final and should be executed.
        struct chain_action_s : controller_event_info_s
        {
            ///Set a chain link to be final, setting a new state and executing a command if the chain is triggered.
            bool b_final = false;

            ///Which of the controller keys are generating the event.
            controller_button_map_t _button = controller_button_map_t::_button_max;
            bool b_state[key_state::_key_state_max] = {false, false, false}; ///<In which states?

            unsigned _next_state = 0; ///<The state to go to (ignored if final == <i>false</i>).
            unsigned _command_index = 0; ///<The command index to execute (ignored if final == <i>false</i>).
        };//end chain_action_s


        EventManager *p_ev_mng_instance = nullptr; ///<Pointer to the EventManager singleton.

        /* Character immediate status */
        unsigned u_current_state = character_state_t::_state_max;///< In which state is the character now?
        unsigned u_active_sprite = 0;///< Sprite index of the animation to use on update
        Character::facing_e _facing_to = FACE_RIGHT;///< Which side of the screen is it facing?
        Character::statistics_s _statistics;///< Character statistics (character sheet)
        const glm::vec3 *vec3p_target = nullptr; ///<Adversary position

        ///Anchor to the character position in world coordinates, must be synchronized to itself and sprites.
        std::shared_ptr<glm::vec3> v3sp_position_anchor = std::make_shared<glm::vec3>(0.f);

        ///The actual implementation of state change machine and handling of status.
        std::vector<AbstractState *> v_states_list;

        ///The actual commands implementation when required to perform an action.
        std::vector<AbstractCommand *> v_command_list;

        ///Map of consecutive inputs that trigger special commands.
        NTree<Character::chain_action_s> nt_chain_actions;
        ///Time out between inputs to reset the chain.
        float f_chain_action_time_out = 0;


        /** @brief Callback to compare the received input event with links in the chain action list. */
        static bool _findInputInChain_cb (Character::chain_action_s &link, controller_button_map_t *key);


    public:
        Character (DomainID_t domain_id, Shader *sh = nullptr,
                   unsigned sprite_count = character_state_t::_state_max,
                   unsigned audio_effect_count = character_state_t::_state_max);
        virtual ~Character ();

        virtual bool setState (unsigned new_state);
        /** @brief Gets the character current state. */
        unsigned getState () const { return this->u_current_state; }

        void setAnimationFPS (float fps, bool loop);
        void setActiveSprite (unsigned index);
        /** @brief Gets the index of the current Sprite being used by the Character. */
        unsigned getActiveSpriteIndex () const { return this->u_active_sprite; }
        /** @brief Gets a reference to the sprite being used by the character in it's current state. */
        Sprite2D& getActiveSprite () const { return this->getSprite(this->u_active_sprite); }

        void playEffect (unsigned index);
        /** @brief Gets the number of audio effects this Character has. */
        unsigned getAudioEffectsCount () const { return this->ui_audio_count; }
        /** @brief Gets an array of all the audio effects contained in this Character. */
        Audio::Effect* getAudioEffects () const { return this->ap_audio_effects; }
        Audio::Effect& getAudioEffect (unsigned index) const;


        void setScaleFactor (float s_factor);

        void mapChainAction (std::vector<controller_button_map_t> &chain, std::vector<key_state> &states, unsigned state_to_go, unsigned command_to_exe);

        /** @brief Sets Character statistics (character sheet). */
        void setStatistics (const Character::statistics_s &statis)
                    { memcpy (&this->_statistics, &statis, sizeof (Character::statistics_s)); }
        /** @brief Gets Character statistics (character sheet). */
        Character::statistics_s& getStatistics () { return this->_statistics; }

        /** @brief Sets the character target, works better if this is a pointer to the actual character target position. */
        void setTarget (const glm::vec3 *target) { this->vec3p_target = target; }
        Character::facing_e faceTarget ();
        /** @brief Which side of the screen the Character should face? */
        void faceTo (Character::facing_e face_to) { this->_facing_to = face_to; }
        /** @brief Which side of the screen is the Character facing? */
        Character::facing_e facing () const { return this->_facing_to; }

        virtual void handleInput (controller_button_map_t input, key_state input_st, double delta_t);
        virtual void execute (unsigned action, double delta_t);
        virtual void update (double delta_t) override;
        virtual void draw () override;
};//END Character

#endif //_CHARACTER_HPP_

